//! 全局的值池，用于存储所有值
//!
//!
use std::{collections::HashSet, marker::PhantomData};

#[derive(Debug)]
pub struct Pool<T> {
    values: Vec<Option<T>>,
    free_slots: HashSet<usize>,
    defer_dealloc: Vec<T>,
}

#[derive(Debug, Clone, Copy)]
pub struct PoolEntry<T>(usize, PhantomData<T>);

impl<T> PoolEntry<T> {
    #[inline]
    pub fn new(handle: usize) -> Self {
        Self(handle, PhantomData)
    }

    pub fn to_usize(self) -> usize {
        self.0
    }

    pub fn to_pool_ref(self) -> PoolRef {
        PoolRef(self.0)
    }
}

#[derive(Debug, Clone, Copy)]
pub struct PoolRef(usize);

impl PoolRef {
    pub fn from_u64(idx: u64) -> Self {
        Self(idx as usize)
    }

    pub fn to_usize(self) -> usize {
        self.0
    }

    pub fn to_u64(self) -> u64 {
        self.0 as u64
    }
}

pub trait HasPoolHandle {
    fn fill_handle(&mut self, handle: usize);
}

impl<T> Pool<T> {
    pub fn new() -> Self {
        Self {
            values: Vec::new(),
            free_slots: HashSet::new(),
            defer_dealloc: Vec::new(),
        }
    }

    pub fn alloc_with_handle(&mut self, mut value: T) -> PoolEntry<T>
    where
        T: HasPoolHandle,
    {
        if let Some(reused_slot) = self.free_slots.iter().next().copied() {
            self.free_slots.remove(&reused_slot);
            value.fill_handle(reused_slot + 1);
            self.values[reused_slot] = Some(value);
            PoolEntry(reused_slot + 1, PhantomData)
        } else {
            let slot = self.values.len();
            value.fill_handle(slot + 1);
            self.values.push(Some(value));
            PoolEntry(slot + 1, PhantomData)
        }
    }

    pub fn alloc(&mut self, value: T) -> PoolEntry<T> {
        if let Some(reused_slot) = self.free_slots.iter().next().copied() {
            self.free_slots.remove(&reused_slot);
            self.values[reused_slot] = Some(value);
            PoolEntry(reused_slot + 1, PhantomData)
        } else {
            let slot = self.values.len();
            self.values.push(Some(value));
            PoolEntry(slot + 1, PhantomData)
        }
    }

    pub fn get(&self, handle: PoolEntry<T>) -> Option<&T> {
        if handle.0 == 0 {
            None
        } else {
            self.values[handle.0 - 1].as_ref()
        }
    }

    pub fn get_mut(&mut self, handle: PoolEntry<T>) -> Option<&mut T> {
        if handle.0 == 0 {
            None
        } else {
            self.values[handle.0 - 1].as_mut()
        }
    }

    pub fn free(&mut self, handle: PoolEntry<T>) {
        if handle.0 == 0 {
            return;
        }
        let slot = handle.0 - 1;
        if let Some(v) = self.values.get_mut(slot).and_then(|x| x.take()) {
            self.defer_dealloc.push(v);
            self.values[slot] = None;
            self.free_slots.insert(slot);
        }
    }

    pub fn free_queue(&mut self, handle: PoolEntry<T>) -> Vec<T> {
        self.free(handle);
        std::mem::replace(&mut self.defer_dealloc, Vec::new())
    }
}
